package com.abel.bigwater.kmgw

import com.abel.bigwater.dataserver.model.ServiceConfig
import com.abel.bigwater.kmgw.dao.ConfigDao
import com.abel.bigwater.kmgw.dao.FlowDao
import io.netty.bootstrap.ServerBootstrap
import io.netty.channel.ChannelInitializer
import io.netty.channel.ChannelOption
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioServerSocketChannel
import org.slf4j.LoggerFactory
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Service


/**
 * protocol of H86.
 */
@Service
class IwgServer {

    @Autowired
    var cfgDao: ConfigDao? = null

    @Autowired
    var flowDao: FlowDao? = null

    var cfg: ServiceConfig? = null

    var bossGroup: NioEventLoopGroup? = null
    var workerGroup: NioEventLoopGroup? = null

    /**
     * start in a thread.
     */
    fun startService() {
        Thread {
            startKmingServer()
        }.also {
            it.isDaemon = true
            it.start()
            it.join(100)
        }
    }

    /**
     * stop the service.
     */
    fun stopService() {
        workerGroup?.shutdownGracefully()
        bossGroup?.shutdownGracefully()
        cfg?.running = false

        lgr.info("$IWG_SERVER_NAME 已退出.")
    }

    fun startKmingServer() {
        cfg = cfgDao?.getConfig(IWG_SERVER_CFG_ID)
        cfg!!.running = true

        val port = cfg?.localPort ?: IWG_PORT

        lgr.info("$IWG_SERVER_NAME 启动中...(端口: $port)")
        try {
            /**
             * ServerBootstrap 是一个启动NIO服务的辅助启动类 你可以在这个服务中直接使用Channel
             */
            var b = ServerBootstrap()
            bossGroup = NioEventLoopGroup()
            workerGroup = NioEventLoopGroup()

            /**
             * 这一步是必须的，如果没有设置group将会报java.lang.IllegalStateException: group not
             * set异常
             */
            b = b.group(bossGroup, workerGroup)

            /***
             * ServerSocketChannel以NIO的selector为基础进行实现的，用来接收新的连接
             * 这里告诉Channel如何获取新的连接.
             */
            b = b.channel(NioServerSocketChannel::class.java)

            /***
             * 这里的事件处理类经常会被用来处理一个最近的已经接收的Channel。 ChannelInitializer是一个特殊的处理类，
             * 他的目的是帮助使用者配置一个新的Channel。
             * 也许你想通过增加一些处理类比如NettyServerHandler来配置一个新的Channel
             * 或者其对应的ChannelPipeline来实现你的网络程序。 当你的程序变的复杂时，可能你会增加更多的处理类到pipline上，
             * 然后提取这些匿名类到最顶层的类上。
             */
            b = b.childHandler(object : ChannelInitializer<SocketChannel>() { // (4)
                @Throws(Exception::class)
                public override fun initChannel(ch: SocketChannel) {
                    ch.pipeline().addLast(IwgDecoder(),
                            IwgServerHandler(flowDao!!))
                }
            })

            /***
             * 你可以设置这里指定的通道实现的配置参数。 我们正在写一个TCP/IP的服务端，
             * 因此我们被允许设置socket的参数选项比如tcpNoDelay和keepAlive。
             * 请参考ChannelOption和详细的ChannelConfig实现的接口文档以此可以对ChannelOptions的有一个大概的认识。
             */
            b = b.option(ChannelOption.SO_BACKLOG, 128)

            /***
             * option()是提供给NioServerSocketChannel用来接收进来的连接。
             * childOption()是提供给由父管道ServerChannel接收到的连接，
             * 在这个例子中也是NioServerSocketChannel。
             */
            b = b.childOption(ChannelOption.SO_KEEPALIVE, true)

            /***
             * 绑定端口并启动去接收进来的连接
             */
            val f = b.bind(port).sync()

            /**
             * 这里会一直等待，直到socket被关闭
             */
            f.channel().closeFuture().sync()
        } finally {
            /***
             * 关闭
             */
            workerGroup?.shutdownGracefully()
            bossGroup?.shutdownGracefully()

            lgr.info("$IWG_SERVER_NAME 已退出.")
        }
    }

    companion object {
        private val lgr = LoggerFactory.getLogger(IwgServer::class.java)
        const val IWG_SERVER_NAME = "IWG大表服务"

        const val IWG_PORT = 6136

        const val IWG_SERVER_CFG_ID = "iwgServer"
    }
}